#ifndef __hude_sqlite3_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__
#define __hude_sqlite3_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__

#include <memory>

#include <hude/base/base_type.hpp>
#include <hude/base/optional.hpp>
#include <hude/utils/json.hpp>

#include <sqlite3.h>

namespace dsqlite
{
    /*********************************************************************
     * @brief _item
     */
    template< typename V >
    static inline void _item( V& v, char* s )
    {
        v = hude::from_string_t< V >::get( s );
    }

    /*********************************************************************
     * @brief _trait_imp_t
     */
    template< typename _ret, std::size_t _index >
    struct _trait_imp_t
    {
        static inline void trait( _ret& tp, char** argv )
        {
            _item( std::get<_index>( tp ), argv[_index] );
            _trait_imp_t< _ret, _index - 1U >::trait( tp, argv );
        }
    };

    template< typename _ret >
    struct _trait_imp_t< _ret, 0U >
    {
        static inline void trait( _ret& tp, char** argv )
        {
            _item( std::get<0>( tp ), argv[0] );
        }
    };

    /*********************************************************************
     * @brief _traits_select_t
     */
    template< typename _ret >
    struct _traits_select_t
    {
        static inline bool trait( _ret& ret, int argc, char** argv )
        {
            enum : std::size_t { emSIZE = std::tuple_size<_ret>::value };
            return ( argc >= emSIZE )? _trait_imp_t<_ret, emSIZE - 1U>::trait( ret, argv ), true : false;
        }
    };

    /*********************************************************************
     * @brief sqlite3_t
     */
    class sqlite3_t :
        public std::enable_shared_from_this< sqlite3_t >
    {
    public:
        /*********************************************************************
         * @brief ...
         */
        using ptr_t = std::shared_ptr< sqlite3_t >;

        /*********************************************************************
         * @brief ...
         */
        using cb_ret_t = std::function< int( int, char**, char** ) >;

    public:
        /*********************************************************************
         * @brief ...
         */
        static std::shared_ptr< sqlite3_t > open( const char* fn )
        {
            auto* p = new sqlite3_t{ fn };
            return ( p && p->handler() )? std::shared_ptr< sqlite3_t >( p, close_sqlite3_t{} ) : nullptr;
        }

        /*********************************************************************
         * @brief ...
         */
        ~sqlite3_t()
        {
            this->_close();
        }

        /*********************************************************************
         * @brief ...
         */
        hude::optional_t< bool > table( const char* name )
        {
            std::tuple< int > res;

            auto ret = this->one( res, R"(SELECT count(*) FROM sqlite_master WHERE type="table" AND name = "%s";)", name );

            return ret? hude::optional_t< bool >{} : hude::optional_t< bool >{ !! std::get<0>( res ) };
        }

        /*********************************************************************
         * @brief ...
         */
        sqlite3* handler() const
        {
            return db_;
        }

        /*********************************************************************
         * @brief ...
         */
        template< std::size_t _siz = 0U, typename ... _args >
        hude::optional_t< std::string > exec( cb_ret_t cb, const char* statemant, _args&& ... args )
        {
            char* err;

            hude::_format_t< _siz > stt{ statemant, std::forward< _args >( args ) ... };

            auto user = hude::any_t{ cb };

            auto ret = sqlite3_exec( this->db_, stt.c_str(), cb_sqlite, &user, &err );

            if( ret != SQLITE_OK )
            {
                auto ret = hude::optional_t< std::string >{ err };
                sqlite3_free( err );
                return ret;
            } else {
                return hude::optional_null_t{};
            }
        }

        /*********************************************************************
         * @brief ...
         */
        template< std::size_t _siz = 0U, typename ... _args >
        hude::optional_t< std::string > exec( const char* statemant, _args&& ... args )
        {
            return this->exec( cb_ret_t{}, statemant, std::forward< _args >( args ) ... );
        }

        /*********************************************************************
         * @brief ...
         */
        template< std::size_t _siz = 0U, typename _tp, typename ... _args >
        hude::optional_t< std::string > one( _tp& tp, const char* statemant, _args&& ... args )
        {
            bool rc = true;
            auto cb = [&rc, &tp]( int argc, char** argv, char** ) -> int { rc = _traits_select_t< _tp >::trait( tp, argc, argv ); return 0; };
            auto ret = this->exec<_siz, _args...>( cb, statemant, std::forward< _args >( args ) ... );
            if( ret ) return ret;

            if(rc)
                return hude::optional_null_t{};
            else
                return std::string{ "cat not record to tuple<...>." };
        }

        /*********************************************************************
         * @brief ...
         */
        template< std::size_t _siz = 0U, typename ... _args >
        hude::optional_t< std::string > one_to_json( hude::utils::json_t& jsn, const char* statemant, _args&& ... args )
        {
            auto cb = [&jsn]( int argc, char** argv, char** cols ) -> int
            {
                for( int i = 0; i < argc; ++i )
                {
                    jsn[cols[i]] = argv[i];
                }

                return 0;
            };

            return this->exec<_siz, _args...>( cb, statemant, std::forward< _args >( args ) ... );
        }

        /*********************************************************************
         * @brief ...
         */
        bool empty() const { return db_ == nullptr; }

        /*********************************************************************
         * @brief ...
         */
        operator bool() const { return db_ != nullptr; }

    private:
        /*********************************************************************
         * @brief 智能指针使用的关闭sqlite对象
         */
        struct close_sqlite3_t { void operator()(sqlite3_t* db) { db->_close(); delete db; } };

    private:
        sqlite3_t( const sqlite3_t& );
        sqlite3_t& operator=( const sqlite3_t& );

        /*********************************************************************
         * @brief ...
         */
        sqlite3_t( const char* fn ) : db_( nullptr )
        {
            this->_open( fn );
        }

        /*********************************************************************
         * @brief ...
         */
        void close() { this->_close(); }

        /*********************************************************************
         * @brief ...
         */
        int _open( const char* fn )
        {
            if( db_ ) this->_close();
            auto ret = sqlite3_open( fn, &db_ );
            if( ret ) db_ = nullptr;
            return ret;
        }

        /*********************************************************************
         * @brief ...
         */
        void _close()
        {
            if( db_ ) { sqlite3_close( db_ ); db_ = nullptr; }
        }

        /*********************************************************************
         * @brief cb_sqlite
         */
        static int cb_sqlite(void* user, int argc, char** argv, char** cols )
        {
            auto cb = hude::any_cast< cb_ret_t >( *(hude::any_t*)user );
            return cb? cb( argc, argv, cols ) : 0;
        }

    private:
        /*********************************************************************
         * @brief ...
         */
        sqlite3* db_;

    };

} //namespace dsqlite

namespace hude
{
    namespace utils
    {
        using sqlite3_t = dsqlite::sqlite3_t;
    } //namespace utils
} //namespace hude

#endif //__hude_sqlite3_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__

